﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

using WpfAdmin.Everything.Enums;
using WpfAdmin.Everything.Queryable;

namespace WpfAdmin.Everything {
    public class Everything : IEverything {
        private static int lastReplyId;
        private const uint DefaultSearchFlags = (uint)(
            RequestFlags.Size
            | RequestFlags.FileName
            | RequestFlags.Extension
            | RequestFlags.Path
            | RequestFlags.FullPathAndFileName
            | RequestFlags.DateModified);

        private readonly uint replyId;
        public Everything() {
            if (!EverythingState.IsStarted()) {
                if (!EverythingState.StartService()) {
                    throw new InvalidOperationException("Everything service must be started");
                }
            }
            this.ResultKind = ResultKind.Both;
            Interlocked.Increment(ref lastReplyId);
            this.replyId = (uint)lastReplyId;
            Reset();
        }

        public Everything(int offset, int limit) : this() {
            Offset = offset;
            MaxResults = limit;
        }

        public long Count => EverythingWrapper.Everything_GetNumResults();
        public int ReplyId => (int)this.replyId;

        public ResultKind ResultKind { get; set; }
        public bool MatchCase { get; set; }
        public bool MatchPath { get; set; }
        public bool MatchWholeWord { get; set; }
        public int? MaxResults { get; set; }
        public int? Offset { get; set; }
        public Sort Sort { get; set; }

        public ErrorCode LastErrorCode { get; private set; }

        public void Dispose() {
            this.Reset();
        }

        public void Reset() {
            EverythingWrapper.Everything_Reset();
        }

        public IEnumerable<ISearchResult> Search(IQuery query) {
            using (EverythingWrapper.Lock()) {
                EverythingWrapper.Everything_SetReplyID(this.replyId);
                EverythingWrapper.Everything_SetMatchWholeWord(this.MatchWholeWord);
                EverythingWrapper.Everything_SetMatchPath(this.MatchPath);
                EverythingWrapper.Everything_SetMatchCase(this.MatchCase);
                EverythingWrapper.Everything_SetRequestFlags((uint)((IQueryGenerator)query).Flags | DefaultSearchFlags);

                var searchPattern = this.ApplySearchResultKind(query.ToString());
                EverythingWrapper.Everything_SetSearch(searchPattern);

                if (Offset.HasValue) {
                    EverythingWrapper.Everything_SetOffset((uint)Offset.Value);
                }
                if (MaxResults.HasValue) {
                    EverythingWrapper.Everything_SetMax((uint)MaxResults.Value);
                }
                if (this.Sort != Sort.None) {
                    EverythingWrapper.Everything_SetSort((uint)this.Sort);
                }

                bool success = EverythingWrapper.Everything_Query(true);
                if (!success) {
                    this.LastErrorCode = this.GetError();
                    throw new InvalidOperationException(this.LastErrorCode.GetDescription());
                }

                return this.GetResults();
            }
        }

        private string ApplySearchResultKind(string searchPattern) {
            switch (this.ResultKind) {
                case ResultKind.FileOnly:
                    return $"files: {searchPattern}";
                case ResultKind.FolderOnly:
                    return $"folders: {searchPattern}";
                default:
                    return searchPattern;
            }
        }

        private ErrorCode GetError() {
            return EverythingState.GetLastError();
        }

        private IEnumerable<ISearchResult> GetResults() {
            int numResults = (int)EverythingWrapper.Everything_GetNumResults();
            return Enumerable.Range(0, (int)numResults).Select(x => new SearchResult(x, this.replyId));
        }
    }
}
